home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / swaga_c.zip / ARCHIVES.SWG / 0013_LZW Compression Unit.pas < prev    next >
Pascal/Delphi Source File  |  1993-09-26  |  10KB  |  406 lines

  1. (*
  2. From: IAN HUNTER
  3. Subj: LZW Compression Unit
  4. *)
  5.  
  6. Unit IHLZW;
  7.   {- Unit to handle data compression }
  8. Interface
  9. Const
  10.   StackOverFlow = 1;
  11.   DeniedWrite = 2;
  12. Type
  13.   GetCharFunc = Function (Var Ch : Char) : Boolean;
  14.   PutCharProc = Procedure (Ch : Char);
  15.   LZW = Object
  16.           GetChar : GetCharFunc;
  17.           PutChar : PutCharProc;
  18.           LastError : Word;
  19.           Constructor Init;
  20.           Function Get_Hash_Code (PrevC, FollC : Integer) : Integer;
  21.           Procedure Make_Table_Entry (PrevC, FollC: Integer);
  22.           Procedure Initialize_String_Table;
  23.           Procedure Initialize;
  24.           Function Lookup_String (PrevC, FollC : Integer) : Integer;
  25.           Procedure Get_Char (Var C : Integer);
  26.           Procedure Put_Char (C : Integer);
  27.           Procedure Compress;
  28.           Procedure Decompress;
  29.           End;
  30.  
  31. Implementation
  32. Const
  33.   MaxTab   = 4095;
  34.   No_Prev  = $7FFF;
  35.   EOF_Char = -2;
  36.   End_List = -1;
  37.   Empty    = -3;
  38.  
  39. Type
  40.   AnyStr = String;
  41.   String_Table_Entry = Record
  42.     Used : Boolean;
  43.     PrevChar : Integer;
  44.     FollChar : Integer;
  45.     Next : Integer;
  46.     End;
  47.  
  48. Var
  49.   String_Table : Array [0..MaxTab] Of String_Table_Entry;
  50.   Table_Used     : Integer;
  51.   Output_Code    : Integer;
  52.   Input_Code     : Integer;
  53.   If_Compressing : Boolean;
  54.  
  55. Constructor LZW.Init;
  56. Begin
  57.   LastError := 0;
  58. End;
  59.  
  60. Function LZW.Get_Hash_Code (PrevC, FollC : Integer) : Integer;
  61. Var
  62.   Index  : Integer;
  63.   Index2 : Integer;
  64. Begin
  65.   Index := ((PrevC SHL 5) XOR FollC) AND MaxTab;
  66.   If (Not String_Table [Index].Used)
  67.     Then
  68.       Get_Hash_Code := Index
  69.     Else
  70.       Begin
  71.         While (String_Table[Index].Next <> End_List) Do
  72.           Index := String_Table[Index].Next;
  73.         Index2 := (Index + 101) And MaxTab;
  74.         While (String_Table[Index2].Used) Do
  75.           Index2 := Succ (Index2) AND MaxTab;
  76.         String_Table[Index].Next := Index2;
  77.         Get_Hash_Code := Index2;
  78.       End;
  79. End;
  80.  
  81. Procedure LZW.Make_Table_Entry (PrevC, FollC: Integer);
  82. Begin
  83.   If (Table_Used <= MaxTab )
  84.     Then
  85.       Begin
  86.          With String_Table [Get_Hash_Code (PrevC , FollC)] Do
  87.            Begin
  88.              Used     := True;
  89.              Next     := End_List;
  90.              PrevChar := PrevC;
  91.              FollChar := FollC;
  92.            End;
  93.          Inc (Table_Used);
  94. (*
  95.          IF ( Table_Used > ( MaxTab + 1 ) ) THEN
  96.             BEGIN
  97.                WRITELN('Hash table full.');
  98.             END;
  99. *)
  100.       End;
  101. End;
  102.  
  103. Procedure LZW.Initialize_String_Table;
  104. Var
  105.   I : Integer;
  106. Begin
  107.   Table_Used := 0;
  108.   For I := 0 to MaxTab Do
  109.     With String_Table[I] Do
  110.       Begin
  111.         PrevChar := No_Prev;
  112.         FollChar := No_Prev;
  113.         Next := -1;
  114.         Used := False;
  115.       End;
  116.   For I := 0 to 255 Do
  117.     Make_Table_Entry (No_Prev, I);
  118. End;
  119.  
  120. Procedure LZW.Initialize;
  121. Begin
  122.   Output_Code := Empty;
  123.   Input_Code := Empty;
  124.   Initialize_String_Table;
  125. End;
  126.  
  127. Function LZW.Lookup_String (PrevC, FollC: Integer) : Integer;
  128. Var
  129.   Index  : Integer;
  130.   Index2 : Integer;
  131.   Found  : Boolean;
  132. Begin
  133.   Index := ((PrevC Shl 5) Xor FollC) And MaxTab;
  134.   Lookup_String := End_List;
  135.   Repeat
  136.     Found := (String_Table[Index].PrevChar = PrevC) And
  137.              (String_Table[Index].FollChar = FollC);
  138.     If (Not Found)
  139.       Then
  140.         Index := String_Table [Index].Next;
  141.   Until Found Or (Index = End_List);
  142.   If Found
  143.     Then
  144.       Lookup_String := Index;
  145. End;
  146.  
  147. Procedure LZW.Get_Char (Var C : Integer);
  148. Var
  149.   Ch : Char;
  150. Begin
  151.   If Not GetChar (Ch)
  152.     Then
  153.       C := EOF_Char
  154.     Else
  155.       C := Ord (Ch);
  156. End;
  157.  
  158. Procedure LZW.Put_Char (C : Integer);
  159. Var
  160.   Ch : Char;
  161. Begin
  162.   Ch := Chr (C);
  163.   PutChar (Ch);
  164. End;
  165.  
  166. Procedure LZW.Compress;
  167.   Procedure Put_Code (Hash_Code : Integer);
  168.   Begin
  169.     If (Output_Code = Empty)
  170.       Then
  171.         Begin
  172.           Put_Char ((Hash_Code Shr 4) And $FF);
  173.           Output_Code := Hash_Code And $0F;
  174.         End
  175.       Else
  176.         Begin
  177.           Put_Char (((Output_Code Shl 4) And $FF0) +
  178.                    ((Hash_Code Shr 8) And $00F));
  179.           Put_Char (Hash_Code And $FF);
  180.           Output_Code := Empty;
  181.         End;
  182.   End;
  183.  
  184.  
  185.   Procedure Do_Compression;
  186.   Var
  187.     C : Integer;
  188.     WC : Integer;
  189.     W : Integer;
  190.   Begin
  191.     Get_Char (C);
  192.     W := Lookup_String (No_Prev, C);
  193.     Get_Char (C);
  194.     While (C <> EOF_Char) Do
  195.       Begin
  196.         WC := Lookup_String (W, C);
  197.         If (WC = End_List)
  198.           Then
  199.             Begin
  200.               Make_Table_Entry (W, C );
  201.               Put_Code (W);
  202.               W := Lookup_String (No_Prev, C);
  203.             End
  204.           Else
  205.             W := WC;
  206.         Get_Char( C );
  207.       End;
  208.     Put_Code (W);
  209.   End;
  210.  
  211. Begin
  212.   If_Compressing := True;
  213.   Initialize;
  214.   Do_Compression;
  215. End;
  216.  
  217. Procedure LZW.Decompress;
  218. Const
  219.   MaxStack = 4096;
  220. Var
  221.   Stack : Array [1..MaxStack] Of Integer;
  222.   Stack_Pointer : Integer;
  223.  
  224.   Procedure Push (C : Integer);
  225.   Begin
  226.     Inc (Stack_Pointer);
  227.     Stack [Stack_Pointer] := C;
  228.     If (Stack_Pointer >= MaxStack)
  229.       Then
  230.         Begin
  231.           LastError := 1;
  232.           Exit;
  233.         End;
  234.   End;
  235.  
  236.   Procedure Pop (Var C : Integer);
  237.   Begin;
  238.     If (Stack_Pointer > 0)
  239.       Then
  240.         Begin
  241.           C := Stack [Stack_Pointer];
  242.           Dec (Stack_Pointer);
  243.         End
  244.       Else
  245.         C := Empty;
  246.   End;
  247.  
  248.   Procedure Get_Code (Var Hash_Code : Integer);
  249.   Var
  250.     Local_Buf : Integer;
  251.   Begin
  252.     If (Input_Code = Empty)
  253.       Then
  254.         Begin
  255.           Get_Char (Local_Buf);
  256.           If (Local_Buf = EOF_Char)
  257.             Then
  258.               Begin
  259.                 Hash_Code := EOF_Char;
  260.                 Exit;
  261.               End;
  262.           Get_Char (Input_Code);
  263.           If (Input_Code = EOF_Char)
  264.             Then
  265.               Begin
  266.                 Hash_Code := EOF_Char;
  267.                 Exit;
  268.               End;
  269.           Hash_Code := ((Local_Buf Shl 4) And $FF0) +
  270.                        ((Input_Code Shr 4) And $00F);
  271.           Input_Code := Input_Code And $0F;
  272.         End
  273.       Else
  274.         Begin
  275.           Get_Char (Local_Buf);
  276.           If (Local_Buf = EOF_Char)
  277.             Then
  278.               Begin
  279.                 Hash_Code := EOF_Char;
  280.                 Exit;
  281.               End;
  282.           Hash_Code := Local_Buf + ((Input_Code Shl 8) And $F00);
  283.           Input_Code := Empty;
  284.         End;
  285.   End;
  286.  
  287.   Procedure Do_Decompression;
  288.   Var
  289.     C : Integer;
  290.     Code : Integer;
  291.     Old_Code : Integer;
  292.     Fin_Char : Integer;
  293.     In_Code : Integer;
  294.     Last_Char : Integer;
  295.     Unknown : Boolean;
  296.     Temp_C : Integer;
  297.   Begin
  298.     Stack_Pointer := 0;
  299.     Unknown := False;
  300.     Get_Code (Old_Code);
  301.     Code := Old_Code;
  302.     C := String_Table[Code].FollChar;
  303.     Put_Char (C);
  304.     Fin_Char := C;
  305.     Get_Code (In_Code);
  306.     While (In_Code <> EOF_Char) Do
  307.       Begin
  308.         Code := In_Code;
  309.         If (Not String_Table [Code].Used)
  310.           Then
  311.             Begin
  312.               Last_Char := Fin_Char;
  313.               Code := Old_Code;
  314.               Unknown := TRUE;
  315.             End;
  316.         While (String_Table [Code].PrevChar <> No_Prev) Do
  317.           With String_Table[Code] Do
  318.             Begin
  319.               Push (FollChar);
  320.               If (LastError <> 0)
  321.                 Then
  322.                   Exit;
  323.               Code := PrevChar;
  324.             End;
  325.         Fin_Char := String_Table [Code].FollChar;
  326.         Put_Char (Fin_Char);
  327.         Pop (Temp_C);
  328.         While (Temp_C <> Empty) Do
  329.           Begin
  330.             Put_Char (Temp_C);
  331.             Pop (Temp_C);
  332.           End;
  333.         If Unknown
  334.           Then
  335.             Begin
  336.               Fin_Char := Last_Char;
  337.               Put_Char (Fin_Char);
  338.               Unknown := FALSE;
  339.             End;
  340.         Make_Table_Entry (Old_Code, Fin_Char);
  341.         Old_Code := In_Code;
  342.         Get_Code( In_Code );
  343.       End;
  344.   End;
  345.  
  346. Begin
  347.   If_Compressing := False;
  348.   Initialize;
  349.   Do_Decompression;
  350. End;
  351.  
  352. End.
  353.  
  354. (* *****************************     TEST PROGRAM    ****************** *)
  355.  
  356. Program LZWTest;
  357. { program to demo/test the LZW object }
  358. Uses
  359.   IHLZW;  { Only needs this }
  360. Var
  361.   C : LZW; { The Star of the Show; the Compression Object }
  362.  
  363. {$F+} Function GetTheChar (Var Ch : Char) : Boolean; {$F-}
  364. { Make your GetChar routine's declaration look exactly like this }
  365.  
  366. Begin
  367.   If Not Eof (Input) { End of Input? }
  368.     Then
  369.       Begin
  370.         Read (Input, Ch); { Then read one character into Ch and ... }
  371.         GetTheChar := True; { ... Return True }
  372.       End
  373.     Else
  374.       GetTheChar := False; { Otherwise return False }
  375. End;
  376.  
  377. {$F+} Procedure PutTheChar (Ch : Char); {$F-}
  378. { Make your PutChar routine's declaration look exactly like this }
  379.  
  380. Begin
  381.   Write (Output, Ch); { Write Ch to Output file }
  382. End;
  383.  
  384. Begin
  385.   { Open data files }
  386.   Assign (Input, ''); { Standard Input; requires redirection to be useful }
  387.   Assign (Output, ''); { Standard Output; requires redirection to be useful }
  388.   Reset (Input);
  389.   Rewrite (Output);
  390.   { Can't fail yet -- maybe a descendant could, though... }
  391.   If not C.Init
  392.     Then
  393.       Halt;
  394.   { Assign I/O routines }
  395.   C.GetChar := GetTheChar; { Set LZW's GetChar to routine GetTheChar }
  396.   C.PutChar := PutTheChar; { Set LZW's PutChar to routine PutTheChar }
  397.   { are we compressing or decompressing? }
  398.   If (ParamCount = 0)
  399.     Then
  400.       C.Compress { compress }
  401.     Else
  402.       C.Decompress; { decompress }
  403.   { All Done! }
  404. End.
  405.  
  406.